package com.example.aidocengine.processor;

import com.example.aidocengine.common.exception.BusinessException;
import com.example.aidocengine.common.result.ResultCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xwpf.usermodel.*;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Word文档处理器
 * 负责处理docx文件的读取、修改和保存
 */
@Slf4j
@Component
public class WordDocumentProcessor {

    private static final Pattern DEVICE_PATTERN = Pattern.compile("([\\u4e00-\\u9fa5]+(?:变|线|母线|开关|刀闸|PT|CT)[^\\s，。；：！？]*)", Pattern.UNICODE_CHARACTER_CLASS);
    
    private static final Pattern OPERATION_STEP_PATTERN = Pattern.compile("(\\d+[、.]\\s*[^\\n]+(?:\\n(?!\\d+[、.])[^\\n]*)*)", Pattern.MULTILINE);
    
    private static final Pattern VOLTAGE_PATTERN = Pattern.compile("(\\d+(?:\\.\\d+)?kV)", Pattern.CASE_INSENSITIVE);

    /**
     * 解析启动方案Word文档，定位第七部分表格并提取操作步骤数据
     */
    public Map<String, Object> parseStartupPlan(MultipartFile file) {
        log.info("开始解析启动方案Word文档：{}", file.getOriginalFilename());
        
        Map<String, Object> result = new HashMap<>();
        List<OperationStepData> operationSteps = new ArrayList<>();
        List<String> extractedText = new ArrayList<>();
        Set<String> deviceNames = new HashSet<>();
        Set<String> voltageLevels = new HashSet<>();
        
        try (InputStream inputStream = file.getInputStream()) {
            XWPFDocument document = new XWPFDocument(inputStream);
            
            StringBuilder fullText = new StringBuilder();
            boolean foundSection7 = false;
            int tableIndex = 0;
            
            // 先提取所有文本内容
            for (XWPFParagraph paragraph : document.getParagraphs()) {
                String paragraphText = paragraph.getText().trim();
                if (!paragraphText.isEmpty()) {
                    extractedText.add(paragraphText);
                    fullText.append(paragraphText).append("\n");
                    
                    // 查找第七部分标识
                    if (paragraphText.contains("第七部分") || paragraphText.contains("启动前有关厂站") || 
                        paragraphText.contains("二次设备运行方式调整") || paragraphText.contains("状态确认")) {
                        foundSection7 = true;
                        log.info("找到第七部分标识：{}", paragraphText);
                    }
                }
            }
            
            // 处理表格，重点关注第七部分相关表格
            for (XWPFTable table : document.getTables()) {
                boolean isSection7Table = false;
                
                // 检查表格是否为标准操作步骤表格
                if (!table.getRows().isEmpty()) {
                    XWPFTableRow headerRow = table.getRows().get(0);
                    String headerText = getRowText(headerRow);
                    
                    // 精确匹配标准表头："序号，受令单位，操作步骤，发令人，发令时间，操作人，完成时间，备注"
                    if (isStandardOperationTable(headerText)) {
                        isSection7Table = true;
                        log.info("找到标准操作步骤表格，表格索引：{}, 表头：{}", tableIndex, headerText);
                    } else {
                        log.debug("跳过非标准表格，表格索引：{}, 表头：{}", tableIndex, headerText);
                    }
                }
                
                // 处理表格数据
                List<XWPFTableRow> rows = table.getRows();
                for (int rowIndex = 0; rowIndex < rows.size(); rowIndex++) {
                    XWPFTableRow row = rows.get(rowIndex);
                    String rowText = getRowText(row);
                    extractedText.add(rowText);
                    fullText.append(rowText).append("\n");
                    
                    // 如果是第七部分表格且不是表头行，提取操作步骤数据
                    if (isSection7Table && rowIndex > 0) {
                        OperationStepData stepData = extractOperationStepFromRow(row, tableIndex, rowIndex);
                        if (stepData != null) {
                            operationSteps.add(stepData);
                            log.debug("提取操作步骤：受令单位={}, 操作步骤={}", 
                                     stepData.getCommandingUnit(), stepData.getOperationStep());
                        }
                    }
                }
                tableIndex++;
            }
            
            document.close();
            
            // 提取设备名称和电压等级
            deviceNames = extractDeviceNames(fullText.toString());
            voltageLevels = extractVoltageLevels(fullText.toString());
            
            result.put("operationSteps", operationSteps);
            result.put("extractedText", extractedText);
            result.put("deviceNames", deviceNames);
            result.put("voltageLevels", voltageLevels);
            result.put("fullText", fullText.toString());
            result.put("foundSection7", foundSection7);
            
            log.info("文档解析完成，找到第七部分：{}, 提取到 {} 个操作步骤，{} 段文本，{} 个设备名称，{} 个电压等级", 
                    foundSection7, operationSteps.size(), extractedText.size(), deviceNames.size(), voltageLevels.size());
            
            return result;
            
        } catch (IOException e) {
            log.error("Word文档解析失败：{}", e.getMessage());
            throw new BusinessException(ResultCode.FILE_PARSE_ERROR.getCode(), "文档解析失败：" + e.getMessage());
        }
    }

    /**
     * 解析Word文档并提取表格数据
     */
    public XWPFDocument parseWordDocument(MultipartFile file) throws IOException {
        log.info("开始解析Word文档: {}", file.getOriginalFilename());
        try (InputStream inputStream = file.getInputStream()) {
            return new XWPFDocument(inputStream);
        }
    }

    /**
     * 从文件路径加载Word文档
     */
    public XWPFDocument loadWordDocument(String filePath) throws IOException {
        log.info("从文件路径加载Word文档: {}", filePath);
        try (FileInputStream fis = new FileInputStream(filePath)) {
            return new XWPFDocument(fis);
        }
    }

    /**
     * 在指定表格的指定行的发令人列插入错误信息
     * 
     * @param document Word文档对象
     * @param tableIndex 表格索引（从0开始）
     * @param rowIndex 行索引（从0开始）
     * @param errorMessage 错误信息
     * @param commanderColumnIndex 发令人列的索引（通常是最后一列）
     */
    public void insertErrorMessage(XWPFDocument document, int tableIndex, int rowIndex, 
                                 String errorMessage, int commanderColumnIndex) {
        try {
            List<XWPFTable> tables = document.getTables();
            
            if (tableIndex >= tables.size()) {
                log.warn("表格索引超出范围: {}, 总表格数: {}", tableIndex, tables.size());
                return;
            }

            XWPFTable table = tables.get(tableIndex);
            List<XWPFTableRow> rows = table.getRows();
            
            if (rowIndex >= rows.size()) {
                log.warn("行索引超出范围: {}, 总行数: {}", rowIndex, rows.size());
                return;
            }

            XWPFTableRow row = rows.get(rowIndex);
            List<XWPFTableCell> cells = row.getTableCells();
            
            if (commanderColumnIndex >= cells.size()) {
                log.warn("列索引超出范围: {}, 总列数: {}", commanderColumnIndex, cells.size());
                return;
            }

            XWPFTableCell commanderCell = cells.get(commanderColumnIndex);
            
            // 在发令人单元格中添加错误信息
            // 根据流程要求，直接在发令人列显示红色错误消息
            XWPFParagraph paragraph = commanderCell.getParagraphs().get(0);
            XWPFRun errorRun = paragraph.createRun();
            
            // 设置支持中文的字体
            errorRun.setFontFamily("SimSun"); // 使用宋体的英文名称
            errorRun.setFontSize(12);
            
            // 设置文本，Apache POI 4.x及以上版本通常支持中文
            errorRun.setText(errorMessage != null ? errorMessage : "校验错误");
            
            errorRun.setColor("FF0000"); // 红色
            errorRun.setBold(true); // 加粗显示

            log.info("在表格{}行{}列插入错误信息: {}", tableIndex, rowIndex, errorMessage);
            
        } catch (Exception e) {
            log.error("插入错误信息失败", e);
        }
    }

    /**
     * 批量处理校验结果，在对应位置插入错误信息
     */
    public void processValidationResults(XWPFDocument document, 
                                       List<ValidationResultDetail> validationResults) {
        log.info("开始批量处理校验结果，共{}条", validationResults.size());
        
        for (ValidationResultDetail result : validationResults) {
            if (!result.isPassed()) {
                // 只对校验失败的记录插入错误信息
                insertErrorMessage(
                    document,
                    result.getTableIndex(),
                    result.getRowIndex(),
                    result.getErrorMessage(),
                    result.getCommanderColumnIndex()
                );
            }
        }
        
        log.info("校验结果处理完成");
    }

    /**
     * 保存修改后的Word文档为字节数组
     */
    public byte[] saveModifiedDocument(XWPFDocument document) throws IOException {
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            document.write(baos);
            byte[] result = baos.toByteArray();
            log.info("Word文档保存完成，大小: {} bytes", result.length);
            return result;
        }
    }

    private List<String> extractOperationSteps(String text) {
        List<String> steps = new ArrayList<>();
        Matcher matcher = OPERATION_STEP_PATTERN.matcher(text);
        
        while (matcher.find()) {
            String step = matcher.group(1).trim();
            if (!step.isEmpty()) {
                steps.add(step);
                log.debug("提取操作步骤：{}", step);
            }
        }
        
        return steps;
    }
    
    private Set<String> extractDeviceNames(String text) {
        Set<String> devices = new HashSet<>();
        Matcher matcher = DEVICE_PATTERN.matcher(text);
        
        while (matcher.find()) {
            String device = matcher.group(1).trim();
            if (!device.isEmpty() && device.length() > 1) {
                devices.add(device);
                log.debug("提取设备名称：{}", device);
            }
        }
        
        return devices;
    }
    
    private Set<String> extractVoltageLevels(String text) {
        Set<String> voltages = new HashSet<>();
        Matcher matcher = VOLTAGE_PATTERN.matcher(text);
        
        while (matcher.find()) {
            String voltage = matcher.group(1);
            voltages.add(voltage);
            log.debug("提取电压等级：{}", voltage);
        }
        
        return voltages;
    }
    
    /**
     * 获取表格行的文本内容
     */
    private String getRowText(XWPFTableRow row) {
        StringBuilder rowText = new StringBuilder();
        for (XWPFTableCell cell : row.getTableCells()) {
            String cellText = cell.getText().trim();
            if (!cellText.isEmpty()) {
                rowText.append(cellText).append(" ");
            }
        }
        return rowText.toString().trim();
    }
    
    /**
     * 从表格行中提取操作步骤数据
     */
    private OperationStepData extractOperationStepFromRow(XWPFTableRow row, int tableIndex, int rowIndex) {
        List<XWPFTableCell> cells = row.getTableCells();
        if (cells.size() < 3) {
            return null; // 至少需要3列：序号、受令单位、操作步骤
        }
        
        try {
            String sequenceNumber = "";  // 序号
            String commandingUnit = "";
            String operationStep = "";
            int commanderColumnIndex = findCommanderColumnIndex(row, tableIndex);
            
            // 根据实际表格结构调整列索引
            for (int i = 0; i < cells.size(); i++) {
                String cellText = cells.get(i).getText().trim();
                if (i == 0 && !cellText.isEmpty()) { // 第1列：序号
                    sequenceNumber = cellText;
                } else if (i == 1 && !cellText.isEmpty()) { // 第2列：受令单位
                    commandingUnit = cellText;
                } else if (i == 2 && !cellText.isEmpty()) { // 第3列：操作步骤
                    operationStep = cellText;
                }
            }
            
            // 只要受令单位和操作步骤不为空就创建数据对象（这就是一行需要检查的操作步骤）
            if (!commandingUnit.isEmpty() || !operationStep.isEmpty()) {
                // 对于没有序号的行，使用行号作为临时序号
                String effectiveSequence = sequenceNumber;
                if (effectiveSequence.isEmpty()) {
                    effectiveSequence = String.valueOf(rowIndex);
                }
                
                log.info("✅ 发现操作步骤（总步骤数+1）：序号={}, 受令单位={}, 操作步骤前30字符={}", 
                         effectiveSequence, commandingUnit, 
                         operationStep.length() > 30 ? operationStep.substring(0, 30) + "..." : operationStep);
                return new OperationStepData(tableIndex, rowIndex, effectiveSequence, commandingUnit, operationStep, commanderColumnIndex);
            } else {
                log.debug("❌ 跳过无效行：序号={}, 受令单位={}, 操作步骤是否为空={}", 
                         sequenceNumber, commandingUnit, operationStep.isEmpty());
            }
            
        } catch (Exception e) {
            log.warn("提取表格行数据失败：表格{}, 行{}, 错误：{}", tableIndex, rowIndex, e.getMessage());
        }
        
        return null;
    }
    
    /**
     * 判断表格是否为标准操作步骤表格
     * 标准表头：序号，受令单位，操作步骤，发令人，发令时间，操作人，完成时间，备注
     */
    private boolean isStandardOperationTable(String headerText) {
        if (headerText == null || headerText.trim().isEmpty()) {
            return false;
        }
        
        String cleanHeader = headerText.replaceAll("\\s+", ""); // 移除所有空格
        
        // 检查是否包含所有必需的列
        boolean hasSequence = cleanHeader.contains("序号");
        boolean hasUnit = cleanHeader.contains("受令单位");
        boolean hasStep = cleanHeader.contains("操作步骤");
        boolean hasCommander = cleanHeader.contains("发令人");
        boolean hasTime = cleanHeader.contains("发令时间") || cleanHeader.contains("时间");
        boolean hasOperator = cleanHeader.contains("操作人");
        boolean hasComplete = cleanHeader.contains("完成时间") || cleanHeader.contains("完成");
        boolean hasRemark = cleanHeader.contains("备注");
        
        // 必须包含核心4列：序号、受令单位、操作步骤、发令人
        boolean isStandard = hasSequence && hasUnit && hasStep && hasCommander;
        
        log.debug("表格表头分析：{} -> 标准操作表格：{}", headerText, isStandard);
        
        return isStandard;
    }
    
    /**
     * 验证序号是否为有效的数字序号
     */
    private boolean isValidSequenceNumber(String sequenceStr) {
        if (sequenceStr == null || sequenceStr.trim().isEmpty()) {
            return false;
        }
        
        String cleanStr = sequenceStr.trim();
        
        // 匹配格式：纯数字、数字.、数字、等
        if (cleanStr.matches("^\\d+[、。\\.]?$")) {
            String numberPart = cleanStr.replaceAll("[^0-9]", "");
            if (!numberPart.isEmpty()) {
                try {
                    int sequence = Integer.parseInt(numberPart);
                    // 只接受合理范围内的序号（1-300）
                    return sequence > 0 && sequence <= 300;
                } catch (NumberFormatException e) {
                    return false;
                }
            }
        }
        
        return false;
    }
    
    /**
     * 查找发令人列的索引
     */
    private int findCommanderColumnIndex(XWPFTableRow row, int tableIndex) {
        List<XWPFTableCell> cells = row.getTableCells();
        
        // 检查当前行是否包含"发令人"
        for (int i = 0; i < cells.size(); i++) {
            String cellText = cells.get(i).getText().trim();
            if (cellText.contains("发令人")) {
                log.debug("在表格{}中找到发令人列，索引：{}", tableIndex, i);
                return i;
            }
        }
        
        // 如果没找到，根据实际表格结构推测：
        // 表格结构为：序号 | 受令单位 | 操作步骤 | 发令人 | 发令时间 | 操作人 | 完成时间 | 备注
        // 发令人是第3列（索引为3）
        int estimatedIndex = 3;
        if (cells.size() <= 3) {
            // 如果列数太少，使用最后一列
            estimatedIndex = Math.max(0, cells.size() - 1);
        }
        log.debug("表格{}未找到发令人列，推测索引：{}，总列数：{}", tableIndex, estimatedIndex, cells.size());
        return estimatedIndex;
    }
    
    /**
     * 操作步骤数据类
     */
    public static class OperationStepData {
        private int tableIndex;
        private int rowIndex;
        private String sequenceNumber;
        private String commandingUnit;
        private String operationStep;
        private int commanderColumnIndex;
        
        public OperationStepData(int tableIndex, int rowIndex, String sequenceNumber, String commandingUnit, String operationStep, int commanderColumnIndex) {
            this.tableIndex = tableIndex;
            this.rowIndex = rowIndex;
            this.sequenceNumber = sequenceNumber;
            this.commandingUnit = commandingUnit;
            this.operationStep = operationStep;
            this.commanderColumnIndex = commanderColumnIndex;
        }
        
        public int getTableIndex() { return tableIndex; }
        public int getRowIndex() { return rowIndex; }
        public String getSequenceNumber() { return sequenceNumber; }
        public String getCommandingUnit() { return commandingUnit; }
        public String getOperationStep() { return operationStep; }
        public int getCommanderColumnIndex() { return commanderColumnIndex; }
    }

    /**
     * 提取表格数据用于校验
     */
    public List<OperationStepData> extractOperationSteps(XWPFDocument document) {
        // TODO: 实现从Word表格中提取操作步骤数据的逻辑
        // 这里需要根据具体的Word文档格式来解析
        log.info("提取操作步骤数据");
        return null; // 暂时返回null，后续实现
    }

    /**
     * 校验结果详情类
     */
    public static class ValidationResultDetail {
        private int tableIndex;
        private int rowIndex;
        private int commanderColumnIndex = 5; // 默认发令人列索引，可根据实际调整
        private boolean passed;
        private String errorMessage;
        private String operationStep;

        // Getters and Setters
        public int getTableIndex() { return tableIndex; }
        public void setTableIndex(int tableIndex) { this.tableIndex = tableIndex; }

        public int getRowIndex() { return rowIndex; }
        public void setRowIndex(int rowIndex) { this.rowIndex = rowIndex; }

        public int getCommanderColumnIndex() { return commanderColumnIndex; }
        public void setCommanderColumnIndex(int commanderColumnIndex) { 
            this.commanderColumnIndex = commanderColumnIndex; 
        }

        public boolean isPassed() { return passed; }
        public void setPassed(boolean passed) { this.passed = passed; }

        public String getErrorMessage() { return errorMessage; }
        public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; }

        public String getOperationStep() { return operationStep; }
        public void setOperationStep(String operationStep) { this.operationStep = operationStep; }
    }

}
